package com.gin.no_gay_alliance.utils;

import com.gin.common.utils.StrUtils;
import com.gin.jackson.utils.ObjectUtils;
import com.gin.nga.bbscode.entity.BbsTag;
import com.gin.nga.bbscode.enums.TagName;
import com.gin.nga.enums.NgaDomain;
import com.gin.nga.enums.NgaPhpApi;
import com.gin.nga.response.emote.Emote;
import com.gin.nga.response.emote.EmoteGroup;
import com.gin.nga.response.field.Attachment;
import com.gin.nga.utils.AnonymousUtils;
import com.gin.no_gay_alliance.app.MyApplication;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 将BbsCode转换为HTML工具类
 *
 * @author : ginstone
 * @version : v1.0.0
 * @since : 2023/8/23 14:10
 **/
public class BbsCodeUtils {
    public static final String TEMPLATE_FONT = "<span style='font-family: %s;'>%s</span>";
    public static final String TEMPLATE_ALIGN = "<span style='display: block;text-align: %s;'>%s</span>";
    public static final String TEMPLATE_TD = "<td class='nga-td' colspan='%s' rowspan='%s' style='%s'>";
    public static final String TEMPLATE_DICE = "<div class='dice-div'><span class='dice-result'>%s</span></div>";
    private static final String TEMPLATE_COLLAPSE = "<div class=\"collapse_border\"><button class='collapse_button'>+</button>%s<div class=\"collapse_content\">%s</div></div>";
    private static final String TEMPLATE_IMG = "<img src='%s' alt='loading' class='loading' onload='imageOnLoad(event)' onclick='android.onClickImg(\"%s\")'/>";
    private static final String TEMPLATE_UID = "https://bbs.nga.cn/nuke.php?func=ucp&uid=%s";
    private static final String TEMPLATE_USERNAME = "https://bbs.nga.cn/nuke.php?func=ucp&__inchst=UTF8&username=%s";
    private static final String TEMPLATE_SIZE = "<span style='font-size: %s'>%s</span>";
    private static final String TEMPLATE_TABLE = "<table class='nga-table'><tbody>%s</tbody></table>";
    /**
     * 表情分组，用于替换表情代码
     */
    public static List<EmoteGroup> EMOTE_GROUPS;

    /**
     * 将BbsCode列表转换为TextView可识别的HTML
     *
     * @param tags
     * @return
     */
    public static String toHtml4TextView(List<BbsTag> tags) {
        if (ObjectUtils.isEmpty(tags)) {
            return "";
        }
        return tags.stream().map(BbsCodeUtils::toHtml4TextView).collect(Collectors.joining());
    }

    /**
     * 将BbsCode转换为TextView可识别的HTML
     *
     * @param tag
     * @return
     */
    public static String toHtml4TextView(BbsTag tag) {
        String params = tag.getParams();
        switch (tag.getName()) {
            case pid: {
                return toPid(tag);
            }
            case uid: {
                if (tag.getRaw().contains(AnonymousUtils.ANONYMOUS_PREFIX)) {
                    //是匿名
                    return AnonymousUtils.getAnonymousName(tag.getChildren().get(0).getText());
                }
                String url = String.format(TEMPLATE_UID, params);
                return toLink(toHtml(tag.getChildren()), url);
            }
            case tid: {
                String url = buildNgaUrl(NgaPhpApi.read, "tid", params);
                return toLink(toHtml(tag.getChildren()), url);
            }
            case text:
                return replaceEmote(tag.getText());
        }
        // 未实现的标签 , 原样输出，内部继续渲染
        String tagName = tag.getName().name;
        return String.format("[%s]%s[/%s]", tagName, toHtml(tag.getChildren()), tagName);
    }


    /**
     * 将将BbsCode列表转换为HTML
     *
     * @param tags BbsCode列表
     * @return HTML
     */
    public static String toHtml(List<BbsTag> tags) {
        if (ObjectUtils.isEmpty(tags)) {
            return "";
        }
        return tags.stream().map(BbsCodeUtils::toHtml).collect(Collectors.joining());
    }

    /**
     * 将单个标签转换为HTML
     *
     * @param tag BbsCode
     * @return HTML
     */
    public static String toHtml(BbsTag tag) {
        String params = tag.getParams();
        switch (tag.getName()) {
            case attach:
                return buildAttachTag(tag);
            case heading:
                return String.format("<h3>%s</h3>", toHtml(tag.getChildren()));
            case list:
                return String.format("<ul>%s</ul>", toHtml(tag.getChildren()));
            case li: {
                String innerCode = toHtml(tag.getChildren());
                // 如果内容为空, 返回空串
                if (ObjectUtils.isEmpty(innerCode.replace("<br/>", "").trim())) {
                    return "";
                }
                return String.format("<li>%s</li>", innerCode);
            }
            case quote:
                return String.format("<div class='nga-quote'>%s</div>", toHtml(tag.getChildren()));
            case randomblock:
                break;
            case randomblocks:
                break;
            case code:
                return getCodeTag(tag);
            case flash: {
                String url = getImgUrl(getUrlFromChild(tag));
                return String.format("<iframe src='%s' sandbox></iframe>", url);
//                return toLink("点击播放音视频", url);
            }
            case collapse: {
                String title = ObjectUtils.isEmpty(params) ? "折叠内容.." : params;
                return String.format(TEMPLATE_COLLAPSE, title, toHtml(tag.getChildren()));
            }
            case img: {
                String url = getImgUrl(tag.getChildren().get(0).getText());
                return String.format(TEMPLATE_IMG, url, url);
            }
            case del:
                return String.format("<del>%s</del>", toHtml(tag.getChildren()));
            case bold:
                return String.format("<b>%s</b>", toHtml(tag.getChildren()));
            case underline:
                return String.format("<u>%s</u>", toHtml(tag.getChildren()));
            case italic:
                return String.format("<i>%s</i>", toHtml(tag.getChildren()));
            case pid: {
                return toPid(tag);
            }
            case uid: {
                if (tag.getRaw().contains(AnonymousUtils.ANONYMOUS_PREFIX)) {
                    //是匿名
                    return AnonymousUtils.getAnonymousName(tag.getChildren().get(0).getText());
                }
                String url = String.format(TEMPLATE_UID, params);
                return toLink(toHtml(tag.getChildren()), url);
            }
            case tid: {
                String url = buildNgaUrl(NgaPhpApi.read, "tid", params);
                return toLink(toHtml(tag.getChildren()), url);
            }
            case url: {
                return toLink(toHtml(tag.getChildren()), getUrl(tag));
            }
            case font:
                return String.format(TEMPLATE_FONT, tag.getParams(), toHtml(tag.getChildren()));
            case color:
                return String.format("<span style='color: %s'>%s</span>", params, toHtml(tag.getChildren()));
            case table: {
                List<BbsTag> children = tag.getChildren().stream().filter(i -> i.getName() == TagName.tr).collect(Collectors.toList());
                return String.format(TEMPLATE_TABLE, toHtml(children));
            }
            case size:
                return String.format(TEMPLATE_SIZE, params, toHtml(tag.getChildren()));
            case tr: {
                List<BbsTag> children = tag.getChildren().stream().filter(i -> i.getName() == TagName.td).collect(Collectors.toList());
                return String.format("<tr>%s</tr>", toHtml(children));
            }
            case right:
                return String.format("<span class='float-right'>%s</span>", toHtml(tag.getChildren()));
            case left:
                return String.format("<span class='float-left'>%s</span>", toHtml(tag.getChildren()));
            case td: {
                String prefix = buildTdTag(tag.getParams());
                return prefix + toHtml(tag.getChildren()) + "</td>";
            }
            case dice:
                return String.format(TEMPLATE_DICE, toHtml(tag.getChildren()));
            case align:
                return String.format(TEMPLATE_ALIGN, params, toHtml(tag.getChildren()));
            case at: {
                String title = String.format("[@%s]", params);
                // 纯数字@
                if (StrUtils.isNumber(params)) {
                    return toLink(title, String.format(TEMPLATE_UID, params));
                }
                return toLink(title, String.format(TEMPLATE_USERNAME, params));
            }
            case text:
                return replaceEmote(tag.getText());
        }
        // 未实现的标签 , 原样输出，内部继续渲染
        String tagName = tag.getName().name;
        return String.format("[%s]%s[/%s]", tagName, toHtml(tag.getChildren()), tagName);
    }

    /**
     * 替换表情code为图片
     *
     * @param raw
     * @return
     */
    public static String replaceEmote(String raw) {
        if (EMOTE_GROUPS == null) {
            return raw;
        }
        // 表情
        List<Emote> emotes = EMOTE_GROUPS.stream().flatMap(i -> i.getEmotes().stream()).collect(Collectors.toList());
        for (Emote emote : emotes) {
            raw = raw.replace(emote.getBbsCode(), String.format(Locale.CHINA, "</span><img src='%s' class='emote' /><span>", emote.getUrl()));
        }
        return String.format(Locale.CHINA, "<span>%s</span>", raw);
    }

    private static String buildAttachTag(BbsTag tag) {
        String url = tag.getChildren().get(0).getText();
        url = getImgUrl(url);
        String title = "下载附件";
        String keyword = "filename=";
        if (url.contains(keyword)) {
            String filename = url.substring(url.indexOf(keyword) + keyword.length());
            try {
                filename = URLDecoder.decode(filename, "utf-8");
                title += ": " + filename;
            } catch (UnsupportedEncodingException ignored) {
            }
        }
        return toLink(title, url);
    }

    /**
     * 生成NGA内部URL
     *
     * @param api    api
     * @param params 参数
     * @return url
     */
    private static String buildNgaUrl(NgaPhpApi api, Map<String, Serializable> params) {
        return buildNgaUrl(MyApplication.getClient().getNgaDomain(), api, params);
    }

    /**
     * 生成NGA内部URL
     *
     * @param domain 域名
     * @param api    api
     * @param params 参数
     * @return url
     */
    private static String buildNgaUrl(NgaDomain domain, NgaPhpApi api, Map<String, Serializable> params) {
        String p = params.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
        return domain.domain + api.path + "?" + p;
    }

    /**
     * 生成NGA内部URL
     *
     * @param api api
     * @return url
     */
    private static String buildNgaUrl(NgaPhpApi api, String key, Serializable value) {
        HashMap<String, Serializable> params = new HashMap<>();
        params.put(key, value);
        return buildNgaUrl(api, params);
    }

    private static String buildTdTag(String params) {
        if (ObjectUtils.isEmpty(params)) {
            return "<td class='nga-td'>";
        }
        int colspan = 1;
        int rowspan = 1;
        String style = "";
        for (String attr : params.split(" ")) {
            if (attr.startsWith("width")) {
                int width = Integer.parseInt(attr.replace("width", ""));
                style = "width: " + width + "%";
            }
            if (attr.startsWith("colspan")) {
                colspan = Integer.parseInt(attr.replace("colspan", ""));
            }
            if (attr.startsWith("rowspan")) {
                rowspan = Integer.parseInt(attr.replace("rowspan", ""));
            }
        }
        return String.format(TEMPLATE_TD, colspan, rowspan, style);
    }

    private static String getCodeTag(BbsTag tag) {
        if (ObjectUtils.isEmpty(tag.getChildren())) {
            return "";
        }
        BbsTag bbsTag = tag.getChildren().get(0);
        String text = bbsTag.getText();
        if (ObjectUtils.isEmpty(text)) {
            return "";
        }
        // 每一行的代码
        StringBuilder sb = new StringBuilder();
        sb.append("<div class='nga-quote'>");
        String params = tag.getParams();
        if (!ObjectUtils.isEmpty(params)) {
            sb.append(String.format("<h4>%s</h4>", params));
        }
        List<String> rows = Arrays.stream(text.replace("<br>", "<br/>").split("<br/>"))
                .filter(i -> !"".equals(i)).collect(Collectors.toList());
        for (int i = 0; i < rows.size(); i++) {
            if ("".equals(rows.get(i).trim())) {
                continue;
            }
            sb.append("<div>");
            // 输出行号
            sb.append(String.format("<span class='row-num'>%s:</span>", i + 1));
            // 输出代码
            String content = String.format("<span class='row-content'>%s</span>", rows.get(i).replace(" ", "&nbsp;"));
            sb.append(content);
            sb.append("</div>");
        }
        sb.append("</div>");
        return sb.toString();
    }

    private static String getImgUrl(String url) {
        url = url.replace("http:", "https:");
        if (url.startsWith("./")) {
            return Attachment.PREFIX + url.substring(2);
        }
        return url.replace("img4.nga.cn", "img4.nga.178.com");
    }

    private static String getUrl(BbsTag tag) {
        String params = tag.getParams();
        if (!ObjectUtils.isEmpty(params)) {
            if (params.startsWith("/")) {
                // 如果为/开头则为站内链接
                String domain = MyApplication.getClient().getNgaDomain().domain;
                return domain + params;
            }
            return params;
        }
        return getUrlFromChild(tag);
    }

    private static String getUrlFromChild(BbsTag tag) {
        BbsTag bbsTag = tag.getChildren().get(0);
        if (bbsTag != null) {
            return bbsTag.getText();
        }
        return "";
    }

    /**
     * 生成a 标签
     *
     * @param title 标题
     * @param url   url
     * @return a标签
     */
    private static String toLink(String title, String url) {
        return String.format("<a href='%s'>%s</a>", url, title);
    }

    /**
     * pid标签渲染
     *
     * @param tag tag
     * @return
     */
    private static String toPid(BbsTag tag) {
        HashMap<String, Serializable> map = new HashMap<>();
        String params = tag.getParams();
        if (ObjectUtils.isEmpty(params)) {
            return tag.getRaw();
        }
        String[] split = params.split(",");
        if (split.length == 0) {
            return tag.getRaw();
        }
        map.put("pid", split[0]);

        if (split.length == 1) {
            map.put("to", 1);
            String url = buildNgaUrl(NgaPhpApi.read, map);
            return toLink(toHtml(tag.getChildren()), url);
        }
        //如果参数有3个, 增加一个跳转链接
        String link1 = toLink("查看", buildNgaUrl(NgaPhpApi.read, map) + "&to=1");
        if (split.length > 2) {
            map.put("tid", split[1]);
            map.put("page", split[2]);
        }
        String link2 = toLink("跳转", buildNgaUrl(NgaPhpApi.read, map));


        return link1 + "&nbsp;/&nbsp;" + link2;
    }
}
